using Mbl.ParameterizedTest.FluentCollectionBuilding.RowCollectors;
using System;

namespace Mbl.ParameterizedTest.FluentCollectionBuilding 
{
    public class ParameterizedTestContext<TSut>
    {
        private readonly Func<TSut> _setup;        

        public ParameterizedTestContext(Func<TSut> setup)
        {
            _setup = setup;
        }

        public GeneralTestRowCollector<TIn1, TOut> For<TIn1, TOut>(Func<TSut, Func<TIn1, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1> For<TIn1>(Func<TSut, Func<TIn1, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1> For<TIn1>(Func<TSut, Func<TIn1, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TOut> CreateTestCollection<TIn1, TOut>(Func<TSut, Func<TIn1, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1);
            }
        }


        public GeneralTestRowCollector<TIn1, TIn2, TOut> For<TIn1, TIn2, TOut>(Func<TSut, Func<TIn1, TIn2, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TIn2, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1, TIn2> For<TIn1, TIn2>(Func<TSut, Func<TIn1, TIn2, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1, TIn2>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1, TIn2> For<TIn1, TIn2>(Func<TSut, Func<TIn1, TIn2, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1, TIn2>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TIn2, TOut> CreateTestCollection<TIn1, TIn2, TOut>(Func<TSut, Func<TIn1, TIn2, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TIn2, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TIn2, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TIn2, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TIn2, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TIn2, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1, TIn2 in2)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1, in2);
            }
        }


        public GeneralTestRowCollector<TIn1, TIn2, TIn3, TOut> For<TIn1, TIn2, TIn3, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TIn2, TIn3, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1, TIn2, TIn3> For<TIn1, TIn2, TIn3>(Func<TSut, Func<TIn1, TIn2, TIn3, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1, TIn2, TIn3>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1, TIn2, TIn3> For<TIn1, TIn2, TIn3>(Func<TSut, Func<TIn1, TIn2, TIn3, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1, TIn2, TIn3>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TIn2, TIn3, TOut> CreateTestCollection<TIn1, TIn2, TIn3, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TIn2, TIn3, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TIn2, TIn3, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TIn2, TIn3, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TIn2, TIn3, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TIn2, TIn3, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1, TIn2 in2, TIn3 in3)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1, in2, in3);
            }
        }


        public GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TOut> For<TIn1, TIn2, TIn3, TIn4, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1, TIn2, TIn3, TIn4> For<TIn1, TIn2, TIn3, TIn4>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1, TIn2, TIn3, TIn4>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1, TIn2, TIn3, TIn4> For<TIn1, TIn2, TIn3, TIn4>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1, TIn2, TIn3, TIn4>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TIn2, TIn3, TIn4, TOut> CreateTestCollection<TIn1, TIn2, TIn3, TIn4, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TIn2, TIn3, TIn4, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TIn2, TIn3, TIn4, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TIn2, TIn3, TIn4, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1, in2, in3, in4);
            }
        }


        public GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> For<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5> For<TIn1, TIn2, TIn3, TIn4, TIn5>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5> For<TIn1, TIn2, TIn3, TIn4, TIn5>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> CreateTestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4, TIn5 in5)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1, in2, in3, in4, in5);
            }
        }


        public GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> For<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> methodUnderTest)
        {            
            return new GeneralTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(CreateTestCollection(methodUnderTest));
        }

        public IntTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6> For<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, int>> methodUnderTest)
        {            
            return new IntTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6>(CreateTestCollection(methodUnderTest));
        }

		public StringTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6> For<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, string>> methodUnderTest)
        {            
            return new StringTestRowCollector<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6>(CreateTestCollection(methodUnderTest));
        }

        private TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> CreateTestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> methodUnderTest)
        {
            var context = new SutContext<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(_setup, methodUnderTest);
            return new TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(context.Setup, context.MethodUnderTest);
        }

        private class SutContext<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>
        {
            private readonly Func<TSut> _setup;
            private readonly Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> _methodUnderTestProvider;

            private TSut _sut;

            public SutContext(Func<TSut> setup, Func<TSut, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> methodUnderTestProvider)
            {
                _setup = setup;
                _methodUnderTestProvider = methodUnderTestProvider;
            }

            public void Setup()
            {
                _sut = _setup();
            }

            public TOut MethodUnderTest(TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4, TIn5 in5, TIn6 in6)
            {
                var methodUnderTest = _methodUnderTestProvider(_sut);
                return methodUnderTest(in1, in2, in3, in4, in5, in6);
            }
        }

    }
}